#include "StarlabMainWindow.h"

#include <QGLWidget>
#include <QFileOpenEvent>

#include "interfaces/GuiPlugin.h"
#include "interfaces/DrawAreaPlugin.h"
#include "ModelRenderer.h"

/// This is the default plugin that gets loaded when no other plugin exists!
class DefaultDrawAreaPlugin : public DrawAreaPlugin{
    class DummyDrawArea : public StarlabDrawArea{
    private:
        class MyGLWidget : public QGLWidget{
        protected:
            virtual void paintGL(){ 
                glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
            }
        } *_widget;
        
    public:
        DummyDrawArea(StarlabMainWindow* /*mw*/, Document* doc, StarlabSettings* set) : StarlabDrawArea(doc,set){
            _widget = new MyGLWidget();
        }
        QGLWidget* widget(){ return _widget; }    
        void resetView(){}        
    };
    StarlabDrawArea* load(StarlabMainWindow* mw, Document*doc, StarlabSettings*set){ 
        return new DummyDrawArea(mw, doc,set); 
    }
};

StarlabMainWindow::StarlabMainWindow(StarlabApplication& _application, StarlabSettings& _settings) 
    : _drawArea(NULL), application(_application), settings(_settings), document(_application.document), pluginManager(_application.plugins){
    
    /// Sets window icon/name
    {
        QIcon icon;
        icon.addPixmap(QPixmap(":images/eye48.png"));
        setWindowIcon(icon);
        setWindowTitle("Starlab v1.0 (alpha)");        
    }
    
    /// Instantiate Menus (plugins will fill them in)
    /// Do not use the silly & windows notation for alt navigation
    {
        fileMenu    = menuBar()->addMenu(tr("File"));
        editMenu    = menuBar()->addMenu(tr("Edit"));
        filterMenu  = menuBar()->addMenu(tr("Filters"));
        modelMenu   = menuBar()->addMenu(tr("Selection"));
        viewMenu    = menuBar()->addMenu(tr("View"));
        windowsMenu = menuBar()->addMenu(tr("Windows"));
        helpMenu    = menuBar()->addMenu(tr("Help"));
        
        // editMenu->clear();
        
        /// @todo is there a better way to retrieve all sub-menus?
        /// I attempted "foreach(QAction* menu_a, menuBar()->actions()){...}"
        menus << fileMenu << editMenu << filterMenu 
              << modelMenu << viewMenu << windowsMenu 
              << helpMenu;

//        /// @todo Remove "Special Charaters" auto entry
//        #ifdef Q_OS_MAC
//            #include <Foundation/NSUserDefaults.h>
//            [[NSUserDefaults standardUserDefaults]
//                setBool:YES forKey:@"NSDisabledCharacterPaletteMenuItem"];
//        #endif
    }
    
    /// Instantiate Toolbars
    {
        mainToolBar = addToolBar(tr("Main Toolbar"));
        editToolBar = addToolBar(tr("Edit Plugins"));
        modelToolBar = addToolBar(tr("Selected Model"));
        filterToolBar = addToolBar(tr("Filter Plugins"));

        /// @todo Why was this necessary in MeshLab?
        // mainToolBar->setIconSize(QSize(32,32)); 
        
        /// @todo is there a better way to retrieve all sub-toolbars?
        toolbars << mainToolBar << editToolBar << modelToolBar << filterToolBar;        
    }
    
    /// Sets up progress/status bar    
    {
        _progressBar = new QProgressBar(this);
        _statusBar   = new QStatusBar(this);
        this->setStatusBar(_statusBar);
        _progressBar->setMaximum(100);
        _progressBar->setMinimum(0);
        _progressBar->setVisible(false);
        _statusBar->addPermanentWidget(_progressBar,0);
        /// By default bottom statusbar is invisible
        _statusBar->setVisible(false);       
        
        /// @brief Whenever there is nothing to show, hide the toolbar.
        /// @todo add a timer that disables it after a while otherwise it's kind of annoying
        connect(_statusBar, SIGNAL(messageChanged(QString)), this, SLOT(hideToolbarOnEmptyMessage(QString)) );
    }
    
    /// Intercepts the "open" events sent directly by the Operative System in this->eventFilter
    {
        setAcceptDrops(true);
    }
    
    /// Install the DrawArea plugins
    {
        DrawAreaPlugin* plugin = NULL;
        if(pluginManager.drawAreaPlugins.size()==0){
            // throw StarlabException("Critical error: Not a single DrawAreaPlugin was loaded!");
            plugin = new DefaultDrawAreaPlugin();
        } else {
            QString preferred = settings.getString("DefaultDrawAreaPlugin");        
            if( !pluginManager.drawAreaPlugins.contains(preferred) ){
                Q_ASSERT( plugin = pluginManager.drawAreaPlugins.begin().value() );
                qDebug() << "Preferred DrawAreaPlugin " << preferred << " was not found. Choosing: " << plugin->name() << " Instead";
            } else
                plugin = pluginManager.drawAreaPlugins[preferred];            
        }
        
        /// Load and Show the Draw Window
        setupDrawArea( plugin->load(this, &document, &settings) );
    }
    
    /// Installs all the GUI plugins
    {
        foreach(GuiPlugin* plugin, pluginManager.guiPlugins)
            plugin->load(this);
    }
    
    /// Makes the window steal the focus (@see QWidget::activateWindow())
    {   
        showNormal();
        activateWindow();
        raise();
    }
    
    /// Delete window on close
    setAttribute(Qt::WA_DeleteOnClose, true);
}

void StarlabMainWindow::dropEvent( QDropEvent * /*event*/ ) {
    throw StarlabException("Drag&Drop not fully implemented");   
#if 0
    //qDebug("dropEvent: %s",event->format());
    const QMimeData * data = event->mimeData();
    if (data->hasUrls()) {
        QList< QUrl > url_list = data->urls();
        for (int i=0, size=url_list.size(); i<size; i++) {
            QString path = url_list.at(i).toLocalFile();
            loadModel(path);
        }
    }
#endif
}

void StarlabMainWindow::triggerFilterByName(QString name){
    name = name.toLower();
    statusBarMessage("Auto-triggering filter: '" + name + "'.");
    bool isFound=false;   
    
    /// Search the "filter" menu for the proper plugin
    foreach(QAction* action, filterMenu->actions())
        if(action->text().toLower()==name){
            isFound=true;
            action->trigger();
        }
    
    if(!isFound){
        statusBarMessage("Filter '"+name+"' was requested but could not be found.");
    }
}

/// @todo Implement this function...
void StarlabMainWindow::triggerMenuActionByName(QString name){
    qDebug() << "TODO: StarlabMainWindow::triggerMenuActionByName( " << name << ")";
}

void StarlabMainWindow::update(){
    // qDebug() << "StarlabMainWindow::update()";
    /// Update all the menus/toolbars
    foreach(GuiPlugin* plugin, pluginManager.guiPlugins)
        plugin->update();
    
    /// Setup and Init rendering
    _drawArea->initRendering();
    
    /// Only display menus and toolbars if they contain something
    {        
        /// Disable empty menus...
        foreach(QMenu* menu, menus){
            // qDebug() << menu->title() << hasactions;
            bool hasactions = menu->actions().size()>0;
            menu->menuAction()->setVisible(hasactions);
        }
        
        /// Disable empty toolbars...
        foreach(QToolBar* tb, toolbars){
            bool hasactions = tb->actions().size()>0;
            tb->setVisible(hasactions);
        }
    }
}

void StarlabMainWindow::updateDrawArea(){
    _drawArea->widget()->update();
}

void StarlabMainWindow::statusBarMessage(QString message, float timeout){
    _statusBar->show();
    _statusBar->showMessage(message, (int)timeout*1000);
    _statusBar->update();
    qDebug() << message;
}

void StarlabMainWindow::progressBarPercentage(QString actor, float completion){
    _statusBar->showMessage(actor); ///< @todo is 0 timeout appropriate?    
    _progressBar->show();
    _progressBar->setEnabled(true);
    _progressBar->setValue(completion);
}

void StarlabMainWindow::hideToolbarOnEmptyMessage(QString /*message*/){
    // qDebug() << "NEW MESSAGE: " << message;
    /// @todo disabled as it's annoying to see it popping in/out
    // if(message.isEmpty())  _statusBar->hide();
}

void StarlabMainWindow::setupDrawArea(StarlabDrawArea *drawArea){        
    Q_ASSERT( drawArea );
    if(drawArea->widget()==NULL)
        throw StarlabException("The DrawAreaPlugin you are attempting to load does not specify a widget");
    StarlabDrawArea* oldDrawArea = _drawArea;
    if(oldDrawArea != NULL)
        delete oldDrawArea; 
    _drawArea = drawArea;
    _drawArea->widget()->setParent(this);
    _drawArea->widget()->setAcceptDrops(true);
    this->setCentralWidget(_drawArea->widget());
    _drawArea->widget()->show();
    /// IBRAHEEM: what should we call to have the stuff 
    /// refresh immediately??    
}

